﻿using KinonekoSoftware.CodariaDB.Languages;
using KinonekoSoftware.CodariaDB.Runtimes.IO;
using KinonekoSoftware.Foundation.Bus;
using DataInitializers = System.Collections.Generic.IReadOnlyCollection<KinonekoSoftware.CodariaDB.Runtimes.Abstractions.IRepositoryInitializer>;
using DataMaintainers = System.Collections.Generic.IReadOnlyCollection<KinonekoSoftware.CodariaDB.Runtimes.Abstractions.IRepositoryMaintainer>;

namespace KinonekoSoftware.CodariaDB.Runtimes
{

    // public delegate void DatabaseInitializeHandler(IRepository repository, string collectionName);

    /// <summary>
    /// 
    /// </summary>
    public delegate void RepositoryOperatingHandler(IRepository Repository, string dir, DataInitializers initializers, DataMaintainers maintainers, bool upgrade);


    /// <summary>
    /// 
    /// </summary>
    public delegate void RepositoryClosingHandler(IRepository Repository, string dir);

    public partial class RepositoryRuntime : Disposable
    {
        private readonly Dictionary<string, Engine>                 _FindByNameMap;
        private readonly Dictionary<Type, Engine>                   _FindByTypeMap;
        private readonly Dictionary<Type, IInstanceAssemblyFactory> _InstanceAssemblyFactory;
        private readonly List<IRepositoryInitializer>               _Initializers;
        private readonly List<IRepositoryMaintainer>                _Maintainers;
        private readonly List<Engine>                               _Engines;
        private readonly ILogger                                    _logger;
        private readonly MessageBus<DB_MSG>                               _queue;

        private Repository _repository;
        private DataMode   _dataMode;
        private Stream     _dbStream;
        private Stream     _logStream;
        private bool       _isOpened;

        private readonly Lazy<ConceptSystem> _lazy_Concept;
        private readonly Lazy<RemoteSystem>  _lazy_Remote;

        private RepositoryRuntime(ILogger logger, IEnumerable<Engine> engines, List<object> initializers, Dictionary<string, Engine> engineMap)
        {
            _logger                  = logger ?? throw new ArgumentNullException(nameof(logger));
            _FindByNameMap           = engineMap;
            _FindByTypeMap           = new Dictionary<Type, Engine>();
            _InstanceAssemblyFactory = new Dictionary<Type, IInstanceAssemblyFactory>();
            _Maintainers             = new List<IRepositoryMaintainer>(initializers.OfType<IRepositoryMaintainer>());
            _Initializers            = new List<IRepositoryInitializer>(initializers.OfType<IRepositoryInitializer>());
            _Engines                 = new List<Engine>(engines);
            _queue                   = new MessageBus<DB_MSG>();
            
            //
            //
            //
            _lazy_Concept = new Lazy<ConceptSystem>(() => _Engines.OfType<ConceptSystem>().FirstOrDefault());
            _lazy_Remote  = new Lazy<RemoteSystem>(() => _Engines.OfType<RemoteSystem>().FirstOrDefault());

            //
            //
            //
            Engines      = new ReadOnlyCollection<Engine>(_Engines);
            Maintainers  = new ReadOnlyCollection<IRepositoryMaintainer>(_Maintainers);
            Initializers = new ReadOnlyCollection<IRepositoryInitializer>(_Initializers);

            Initialize();
        }

        private void Initialize()
        {
            //
            // 添加所有的类型
            _Engines.ForEach(x =>
            {
                //
                //
                _FindByTypeMap.TryAdd(x.GetType(), x);


                //
                // 注册装载工厂
                if (x is IInstanceAssemblyFactory factory)
                {
                    //
                    //
                    factory.Register(_InstanceAssemblyFactory);
                }
            });

            //
            // 初始化所有消息处理器
            InitializeMessageHandlers();
        }


        private void FinishLoading(Repository repository, bool create = false, bool upgrade = false)
        {
            //
            //
            _isOpened = true;

            //
            // 设置日志记录器
            repository.Logger = _logger;

            //
            // 初始化数据
            var initializer = _Initializers.Where(x => x.NeedInitialize(repository)).ToArray();
            var maintainers = _Maintainers.Where(x => x.NeedMaintain(repository)).ToArray();

            var sw = new Stopwatch();
            sw.Start();
            foreach (var engine in Engines)
            {
                engine.Open(new RepositoryOpenContext
                {
                    Repository = repository,
                    Logger     = _logger,
                    Queue      = _queue,
                });

                //
                // 如果为创建世界观操作，则执行初始化。
                if (create) engine.Initialize();
            }
            
            
            sw.Stop();
            ElapsedTime_OpenEngine = sw.ElapsedMilliseconds;
            Opening?.Invoke(repository, repository.Workspace, initializer, maintainers, upgrade);
        }


        #region LoadAsync

        /// <summary>
        /// 用于临时测试的方法
        /// </summary>
        private void LoadOnMemory()
        {
            //
            // 关闭当前的数据库
            Close();

            _dbStream  = new MemoryStream(Codaria.DatabaseInitializeSize);
            _logStream = new MemoryStream();
            _dataMode  = DataMode.Protected;

            var dir    = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "JuXiaoYou");
            var litedb = new LiteDatabase(_dbStream, DatabaseBsonMapper.Override, _logStream);
            var cache  = Path.Combine(dir, Codaria.Repository_IndexerFileName);
            var db     = Path.Combine(dir, Codaria.Repository_MainFileName);
            var database = new Repository(new RepositoryLoadContext
            {
                Mode             = _dataMode,
                Workspace        = dir,
                IndexerFileName  = cache,
                DatabaseFileName = db,
                Database         = litedb,
            });


            _repository = database;

            //
            //
            FinishLoading(_repository);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="mode"></param>
        /// <param name="dir"></param>
        /// <param name="db"></param>
        /// <returns></returns>
        private LiteDatabase CreateFrom(DataMode mode, string dir, string db)
        {
            var sw = new Stopwatch();
            sw.Start();
            
            var logFileName = Path.Combine(dir, Codaria.Repository_LogFileName);

            //
            // 保护模式的创建
            //
            if (mode == DataMode.Protected)
            {
                using var mem = File.OpenRead(db);
                var       ms  = new MemoryStream(Codaria.DatabaseInitializeSize);
                mem.CopyTo(ms);
                ms.Seek(0, SeekOrigin.Begin);

                _dbStream  = ms;
                _logStream = new MemoryStream(16384);

                //
                // 统计加载数据所花费的时间。
                sw.Stop();
                ElapsedTime_LoadDatabase = sw.ElapsedMilliseconds;
                return new LiteDatabase(_dbStream, DatabaseBsonMapper.Override, _logStream);
            }

            //
            // 正常模式的创建
            _dbStream  = File.Open(db, FileMode.OpenOrCreate);
            _logStream = File.Open(logFileName, FileMode.OpenOrCreate);
         
            //
            // 统计加载数据所花费的时间。
            sw.Stop();
            ElapsedTime_LoadDatabase = sw.ElapsedMilliseconds;
            return new LiteDatabase(_dbStream, DatabaseBsonMapper.Override, _logStream);
        }

        public async Task<Result> LoadAsync(string dir, DataMode mode)
        {
            var sw = new Stopwatch();
            sw.Start();
            
            if (string.IsNullOrEmpty(dir))
            {
                //
                //
                return Result.Failed(CodariaSR.Text_Directory_Null);
            }

            try
            {
                if (mode == DataMode.Debug)
                {
                    LoadOnMemory();
                    return Result.Success;
                }

                //
                // 判断文件夹是否为空
                if (!Directory.Exists(dir))
                {
                    //
                    //
                    return Result.Failed(CodariaSR.Text_Directory_NotExits);
                }
                

                var cache = Path.Combine(dir, Codaria.Repository_IndexerFileName);
                var db    = Path.Combine(dir, Codaria.Repository_MainFileName);

                if (!File.Exists(cache) || !File.Exists(db))
                {
                    return Result.Failed(CodariaSR.Text_Directory_NotExits);
                }

                var litedb = CreateFrom(mode, dir, db);

                var database = new Repository(new RepositoryLoadContext
                {
                    Mode             = mode,
                    Workspace        = dir,
                    IndexerFileName  = cache,
                    DatabaseFileName = db,
                    Database         = litedb,
                });

                //
                // 关闭当前的数据库
                await CloseAsync();

                //
                //
                _repository = database;
                _dataMode   = mode;

                FinishLoading(_repository);

                sw.Stop();
                ElapsedTime_Load = sw.ElapsedMilliseconds;
                return Result.Success;
            }
            catch(IOException)
            {
                sw.Stop();
                ElapsedTime_Load = sw.ElapsedMilliseconds;
                return Result.Failed("世界观文件被占用!\n要解决这个问题需要关闭后台正在打开的橘小柚或者其他应用！");
            }
            catch(Exception ex)
            {
                sw.Stop();
                ElapsedTime_Load = sw.ElapsedMilliseconds;
                return Result.Failed(ex.Message);
            }
        }

        #endregion

        #region DumpAsync / BackupAsync

        public async Task DumpAsync()
        {
            //
            // 判断数据库属性是否有变化，如果有泽写入缓存
            if (!File.Exists(_repository.IndexerFileName) || _repository.Version > 0)
            {
                _repository.UpdateProperty();

                //
                // 将新的数据写入缓存
                var snapshot = _repository.Snapshot();

                //
                //
                await JSON.DumpAsync(snapshot, _repository.IndexerFileName, false);
            }

            _repository.Kernel.Checkpoint();
            _repository.Kernel.Checkpoint();

            await using (var fs = File.OpenWrite(_repository.DatabaseFileName))
            {
                var pos = _dbStream.Position;
                _dbStream.Seek(0, SeekOrigin.Begin);
                await _dbStream.CopyToAsync(fs);
                _dbStream.Seek(pos, SeekOrigin.Begin);
            }
        }
        
        
        public static string GetDir(string dir)
        {
            if (!Directory.Exists(dir))
            {
                Directory.CreateDirectory(dir);
            }
            return dir;
        }

        public async Task BackupAsync()
        {
            var currentPosition = _dbStream.Position;
            var backupDirectory = GetDir(Path.Combine(_repository.Workspace, "Backup"));
            var backupFileName  = Path.Combine(backupDirectory, $"{DateTime.Now:yyyy-M-d-HH-mm}.projDB");
            var backupStream    = new FileStream(backupFileName, FileMode.Create);

            _dbStream.Seek(0, SeekOrigin.Begin);
            await _dbStream.CopyToAsync(backupStream);
            _dbStream.Seek(currentPosition, SeekOrigin.Begin);
            await backupStream.DisposeAsync();
        }

        #endregion

        public async Task<Result> CreateAsync(string dir, RepositoryCache property, bool upgrade = false)
        {

            if (string.IsNullOrEmpty(dir))
            {
                //
                //
                return Result.Failed(CodariaSR.Text_Directory_Null);
            }


            try
            {

                if (!upgrade && Directory.GetFiles(dir).Any())
                {
                    //
                    //
                    return Result.Failed(CodariaSR.Text_Directory_NotEmpty);
                }


                _dataMode = DataMode.Release;

                //
                // 判断文件夹是否为空
                if (!upgrade && !Directory.Exists(dir))
                {
                    // 创建文件夹
                    Directory.CreateDirectory(dir);
                }


                var cache = Path.Combine(dir, Codaria.Repository_IndexerFileName);
                var db    = Path.Combine(dir, Codaria.Repository_MainFileName);
                var log   = Path.Combine(dir, Codaria.Repository_LogFileName);
                _dbStream  = File.Open(db, FileMode.OpenOrCreate);
                _logStream = File.Open(log, FileMode.OpenOrCreate);
                var litedb = new LiteDatabase(_dbStream, DatabaseBsonMapper.Override, _logStream);
                var database = new Repository(new RepositoryLoadContext
                {
                    Mode             = _dataMode,
                    Workspace        = dir,
                    IndexerFileName  = cache,
                    DatabaseFileName = db,
                    Database         = litedb,
                });

                database.Id          = property.Id;
                database.Name        = property.Name;
                database.Author      = property.Author;
                database.Intro       = property.Intro;
                database.ForeignName = property.ForeignName;
                property.Path        = dir;

                //
                // update
                database.UpdateProperty();
                database.DatabaseVersion = Codaria.CurrentDatabaseVersion;

                //
                // 关闭当前的数据库
                await CloseAsync();

                //
                //
                _repository = database;
                FinishLoading(_repository, true, upgrade);
                await JSON.DumpAsync(property, _repository.IndexerFileName, false);
                return Result.Success;
            }
            catch(Exception ex)
            {
                return Result.Failed(ex.Message);
            }
        }

        protected override void ReleaseUnmanagedResources()
        {
            if (_repository is not null)
            {
                // Close();
            }
            base.ReleaseUnmanagedResources();
        }

        #region Close / CloseAsync

        public async Task CloseAsync(bool keepMemCacheAlive = false)
        {
            if (_dbStream is not null && keepMemCacheAlive)
            {
                await DumpAsync();
            }
            
            await Task.Run(Close);
        }

        public void Close()
        {
            if (_repository is null)
            {
                return;
            }
            var sw = new Stopwatch();
            sw.Start();

            //
            // 通知外部，数据库关闭
            Closing?.Invoke(_repository, _repository.Workspace);

            //
            // 进入数据库关闭流程
            _repository.Kernel.Checkpoint();
            _repository.Kernel.Checkpoint();

            //
            // 判断数据库属性是否有变化，如果有泽写入缓存
            if (_repository.Version > 0)
            {
                _repository.UpdateProperty();

                //
                // 将新的数据写入缓存
                var snapshot = _repository.Snapshot();

                //
                //
                JSON.Dump(snapshot, _repository.IndexerFileName);
            }

            //
            //
            _repository.Logger = null;


            //
            // Close All Engine
            foreach (var engine in Engines)
            {
                engine.Close(_repository, _logger);
            }

            //
            //
            _repository.Dispose();
            _dbStream?.Dispose();
            _logStream?.Dispose();

            //
            //
            _repository = null;
            _isOpened   = false;
            
            //
            //
            sw.Stop();
            ElapsedTime_Close = sw.ElapsedMilliseconds;
        }

        #endregion

        #region GetEngine

        /// <summary>
        /// 获取指定的引擎
        /// </summary>
        /// <typeparam name="T">引擎的类型</typeparam>
        /// <returns>如果存在则返回指定的引擎，否则返回false。</returns>
        public T GetEngine<T>() where T : Engine
        {
            //
            // find the first and the only engine of T
            // the method complexity is O(N)
            // maybe null value
            var engine = _FindByTypeMap[typeof(T)];

            //
            // return null
            if (engine is null)
            {
                return default(T);
            }

            //
            // return as T
            return (T)engine;
        }


        /// <summary>
        /// 获取指定的引擎
        /// </summary>
        /// <param name="id">引擎ID</param>
        /// <returns>如果存在则返回指定的引擎，否则返回false。</returns>
        public Engine GetEngine(string id)
        {
            if (string.IsNullOrEmpty(id))
            {
                return default(Engine);
            }

            //
            // find the first and  only engine by id
            // the method complexity is O(1)
            // maybe null value
            return _FindByNameMap.GetValueOrDefault(id);
        }

        #endregion

        /// <summary>
        /// 
        /// </summary>
        public IReadOnlyCollection<IRepositoryInitializer> Initializers { get; }

        /// <summary>
        /// 
        /// </summary>
        public IReadOnlyCollection<IRepositoryMaintainer> Maintainers { get; }

        /// <summary>
        /// 
        /// </summary>
        public IReadOnlyCollection<Engine> Engines { get; }

        /// <summary>
        /// 
        /// </summary>
        public RepositoryOperatingHandler Opening { get; set; }

        /// <summary>
        /// 
        /// </summary>
        public RepositoryClosingHandler Closing { get; set; }

        /// <summary>
        /// 
        /// </summary>
        public DataMode Mode => _dataMode;

        /// <summary>
        /// 
        /// </summary>
        public IRepository Repository => _repository;

        public bool IsOpened => _isOpened;
    }
}